<?php
require_once 'class/lib/utils.php';
require_once 'Passage.php';
/**
 * Room class, holds rooms data.
 * @package space
 * @author alcaitiff
 */
class Room extends ExportableObject 
{
	protected $name,$description,$itemsNames=array(),$exits=array(),$charsNames=array();
	private $id;
	/**
     *
     * @param int $id
     */
	public function __construct($id){
		$this->id=$id;
		$this->retrieveMainData();
		$this->retrieveCharsNames();
		$this->retrieveItemsNames();
		$this->retrieveExits();
	}
    /**
     * Simple get
     * @return int
     */
	public function getId(){
		return $this->id;
	}
    /**
     * Search items on floor by name return an array as $key=>$value where $key = id and $value = name
     * @param string $name
     * @param int $max
     * @return array|string[]
     */
	public function getItemsByName($name,$max=null){
		return Utils::arraySearch($this->itemsNames,$name,$max);
	}
    /**
     * Search rooms or doors by direction name
     * @param string $name
     * @return Room|Passage
     */
	public function getDirectionByName($name){
		foreach($this->exits as $key=>$exit){
			if(Utils::startWith($exit->direction,$name)){
				if($exit->door->isOpen())
					return new Room($key);
				else{
					return $exit;
				}
			}
		}
		return null;
	}
    /**
     * Search passages by directions
     * @param Passage $name
     * @return Passage
     */
    public function getPassageByDirection($name){
        foreach($this->exits as $key=>$exit){
			if(Utils::startWith($exit->direction,$name)){
				return $exit;
			}
		}
		return null;
    }

    /**
     * Search chars by name
     * @param string $name
     * @param int $max
     * @return array|Char[]
     */
	public function getCharactersByName($name,$max=null){
		return Utils::arraySearch($this->charsNames,$name,$max);
	}
    /**
     * Switch north <-> south, east <-> west and up <-> down
     * @param string $dir
     * @return string
     * @static
     */
	static public function reverseDir($dir){
		switch($dir){
			case 'east':	return 'west';
							break;
						 	
			case 'west':	return 'east';
							break;
						 	
			case 'up':		return 'down';
							break;
						 	
			case 'down':	return 'up';
							break;
						 	
			case 'north':	return 'south';
							break;
						 	
			case 'south':	return 'north';
							break;
			default:
							return null;
					 	
		}
	}
    /**
     * Send a message to all chars in room that $char leave to $direction
     * @param Char $char Character
     * @param string $direction
     * @return void
     */
	public function leave($char,$direction){
		$this->broadcastMessage($char->charId.' leave to '.$direction,$char);
	}
    /**
     * Send a message to all chars in room that $char arrive from $direction
     * @param Char $char Character
     * @param string $direction
     * @return void
     */
	public function arrive($char,$direction){
		$this->broadcastMessage($char->charId.' arrive from '.$direction,$char);
	}
    /**
     * Send a message to all chars in room except $char
     * @param string|ExportableObject $message
     * @param Char $char Sender
     * @return void
     */
	public function broadcastMessage($message,$char=null){

		$chars=$this->charsNames;
        if($char!==null){
            $key=array_search($char->charId,$chars);
            if($key!==false){
                unset($chars[$key]);
            }
        }
		MessageSystem::broadcastMessage($message,$chars);
	}
    /**
     * Send a message to all chars in this and adjacent rooms except $char
     * @param string|ExportableObject $message
     * @param Char $char Sender
     * @return void
     */
	public function broadcastAreaMessage($message,$char){
        foreach ($this->exits as $key => $value) {
            $room=new Room($key);
            $room->broadcastMessage($message);
        }
        $this->broadcastMessage($message,$char);
	}

    /**
     * Remove an item from room
     * @param Item $item
     * @return bool
     */
    public function removeItem($item){
        return $this->deleteItem($item->getId());
    }
    /**
     * Add an item on floor
     * @param Item $item
     * @return bool 
     */
    public function addItem($item){
        if($item->isStackable()){
            foreach($this->itemsNames as $key=>$old){
                if($old==$item->getType()){
                    $old=new Item($key);
                    if($old->merge($item)){
                        return true;
                        break;
                    }
                }
            }
        }
        return $this->saveItem($item->getId());
    }
    //***********************************************************************************************************
    //					DATABASE METHODS
    //***********************************************************************************************************
	/**
     * Retrieve DB data
     * @return void
     */
	private function retrieveMainData(){
		$dbo=new DB();
		$db=$dbo->db;
		$stmt = $db->prepare(	'SELECT  `description`, '.
										'`name` '.
								'FROM `rooms` '.		
								'WHERE   `room` = ? '.
								'LIMIT 1'
								);
	    $stmt->bind_param("i", $this->id);
	    $stmt->bind_result($this->description,$this->name);
	    $stmt->execute();
	    $stmt->fetch();
  		$stmt->close();
	}
    /**
     * Retrieve chars names
     * @return void
     */
	private function retrieveCharsNames(){
		$dbo=new DB();
		$db=$dbo->db;
		$stmt = $db->prepare(	'SELECT	`char_id` '.
								'FROM `char` '.		
								'WHERE  `room` = ? '.
								'ORDER BY `char_id`'
								);
	    $stmt->bind_param("i", $this->id);
	    $stmt->bind_result($chara);

   		$this->charsNames=array();

	    $stmt->execute();
	    while($stmt->fetch()){
	    	$this->charsNames[]=$chara;
	    }
  		$stmt->close();
	}
    /**
     * Retrieve items names
     * @return void
     */
	private function retrieveItemsNames(){
		$dbo=new DB();
		$db=$dbo->db;
		$stmt = $db->prepare(	'SELECT `items`.`item_type`, items.item '.
								'FROM room_items, items '.
								'WHERE 	room_items.room = ? AND '.
								'		room_items.item = items.item '.
								'ORDER BY `items`.`item_type` '
								);
	    $stmt->bind_param("i", $this->id);
	    $stmt->bind_result($name,$id);
	    
	    $this->itemsNames=array();
	    
	    $stmt->execute();
	    while($stmt->fetch()){
	    	$this->itemsNames[$id]=$name;
	    }
  		$stmt->close();
	}
	/**
     * Retrieve exits
     * @return void
     */
	private function retrieveExits(){
		$dbo=new DB();
		$db=$dbo->db;
		$stmt = $db->prepare(	'SELECT `room_from`, `room_to`, `direction`, `door`'.
								'FROM `passages` '.
								'WHERE  room_from = ? OR room_to = ? '
								);
	    $stmt->bind_param("ii", $this->id,$this->id);
	    $stmt->bind_result($from,$to,$dir,$door);
	    
	    $this->exits=array();
	    
	    $stmt->execute();
	    while($stmt->fetch()){
	    	if($to==$this->id){
	    		$dir=Room::reverseDir($dir);
	    		$to=$from;
	    	}
    		$this->exits[$to]=new Passage($dir,$door);
	    }
  		$stmt->close();
	}
    /**
     * Delete an item from DB
     * @param int $id
     */
    private function deleteItem($id){
        $dbo=new DB();
		$db=$dbo->db;
        $stmt = $db->prepare('DELETE FROM `room_items` WHERE `item`=? and `room`=?');
	    $stmt->bind_param("ii", $id,  $this->id);
	    $stmt->execute();
        $ret = $stmt->affected_rows;
        $stmt->close();
        return $ret;
    }
    /**
     * Save a new item on DB
     * @param int $id
     * @return int
     */
    private function saveItem($id){
        $dbo=new DB();
		$db=$dbo->db;
        $stmt = $db->prepare('INSERT INTO `room_items` (`item`,`room`) VALUES (?,?)');
	    $stmt->bind_param("ii", $id,  $this->id);
	    $stmt->execute();
        $ret = $stmt->affected_rows;
        $stmt->close();
        return $ret;
    }
}